package com.bc004346.travelus.services;

import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.IBinder;
import android.preference.PreferenceManager;

import com.bc004346.travelus.SettingsActivity;
import com.bc004346.travelus.TripMeFlightListActivity;
import com.bc004346.travelus.model.domain.DayTrip;
import com.bc004346.travelus.model.domain.Flight;
import com.bc004346.travelus.model.domain.FlightSearchParams;
import com.bc004346.travelus.model.domain.NotificationParameters;
import com.bc004346.travelus.model.domain.TripMeOptions;
import com.bc004346.travelus.utility.NotificationHelper;
import com.bc004346.travelus.utility.Utility;

import java.util.ArrayList;
import java.util.Date;

import static com.bc004346.travelus.utility.Utility.DAY;
import static com.bc004346.travelus.utility.Utility.getInstance;
import static com.bc004346.travelus.utility.Utility.log;

/**
 * Created by Renats on 11/7/2014.
 * Searches for flights on pre-defined intervals
 */
public class TripMeService extends Service {

    public static final String TRIP_ME_INTENT = "com.bc004346.travelus.services.tripMe";
    public static final String EXTRA_USER = "username";
    public static final String EXTRA_PASSWORD = "password";
    public static final String EXTRA_SENIORITY = "seniority";
    public static final String EXTRA_SCHEDULED_SEARCH = "scheduled";
    public static final int NOTIFICATION_ID = 512;
    public static final String LAST_AUTOMATIC_TRIP_ME_SEARCH = "lastAutomaticTripMeSearch";

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        log("starting...");
        try {
            TripMeOptions options = SettingsActivity.getTripMeOptions(getApplicationContext());
            final String user = intent.getStringExtra(EXTRA_USER);
            final String pass = intent.getStringExtra(EXTRA_PASSWORD);
            final Date senior = (Date) intent.getSerializableExtra(EXTRA_SENIORITY);
            final Boolean isScheduled = intent.getBooleanExtra(EXTRA_SCHEDULED_SEARCH, true);

            if (!validOptions(options, isScheduled)) return Service.START_NOT_STICKY;

            log("initiating flight search");
            if(isScheduled) updateLastTripMeDate();
            search(options, user, pass, senior);
        } catch (Exception e) {
            log(e);
        }

        return Service.START_STICKY;
    }

    private void search(TripMeOptions options, final String user, final String pass, final Date senior) {
        new AsyncTask<TripMeOptions, Void, ArrayList<DayTrip>>() {

            private Exception ex;

            @Override
            protected ArrayList<DayTrip> doInBackground(TripMeOptions... options) {
                ArrayList<DayTrip> trips = new ArrayList<DayTrip>();
                try {
                    TripMeOptions option = options[0];
                    Date x = Utility.DATE.parse(Utility.DATE.format(new Date(new Date().getTime() + DAY)));
                    log("Home base = ", option.getHomeBase());
                    for (String destination : option.getFavoriteDestinations()) {
                        log("Searching flights to / from " + destination);
                        FlightSearchParams params = new FlightSearchParams();
                        params.setOrigin(option.getHomeBase());
                        params.setDestination(destination);
                        params.setIsOneWay(true);
                        params.setDeparture(x);
                        params.setReturn(x);
                        ArrayList<Flight> outboundFlights = getInstance().getTripMe(user, pass, senior)
                                .searchFlights(params);

                        //swap origin and destination to have return flights
                        params.setOrigin(destination);
                        params.setDestination(option.getHomeBase());
                        ArrayList<Flight> inboundFlights = getInstance().getTripMe(user, pass, senior)
                                .searchFlights(params);

                        for (Flight outbound : outboundFlights)
                            if (outbound.getOrigin().equalsIgnoreCase(option.getHomeBase())
                                    && option.getFavoriteDestinations().contains(outbound.getDestination()))
                                for (Flight inbound : inboundFlights)
                                    if (inbound.getDestination().equalsIgnoreCase(option.getHomeBase())
                                            && option.getFavoriteDestinations().contains(inbound.getOrigin())
                                            && isAGoodTrip(outbound, inbound))
                                        trips.add(new DayTrip(outbound, inbound));


                    }
                } catch (Exception e) {
                    ex = e;
                }
                return trips;
            }

            @Override
            protected void onPostExecute(ArrayList<DayTrip> dayTrips) {
                if (ex == null) {

                    if (dayTrips.size() == 0) {
                        NotificationParameters params = new NotificationParameters(TripMeService.this,
                                null,
                                TRIP_ME_INTENT,
                                "Trip Me",
                                "Unfortunately no good flight options were found. Try adding destinations or try again tomorrow",
                                null, NOTIFICATION_ID);
                        NotificationHelper.displayNotification(params);
                    } else {
                        for (DayTrip trip : dayTrips) {
                            log("=============");
                            log(trip.toString());
                            log("Outbound departure: " + Utility.DATE_TIME.format(trip.getOutboundFlight().getDeparture())
                                    + "; SA: " + String.valueOf(trip.getOutboundFlight().getCoach().getSeatsAvailable()));
                            log("Inbound departure: " + Utility.DATE_TIME.format(trip.getInboundFlight().getDeparture())
                                    + "; SA: " + String.valueOf(trip.getInboundFlight().getCoach().getSeatsAvailable()));
                        }

                        NotificationParameters params = new NotificationParameters(TripMeService.this,
                                dayTrips,
                                TRIP_ME_INTENT,
                                "Trip Me",
                                String.format("Here are %d day trip%s for you!",
                                        dayTrips.size(),
                                        dayTrips.size() > 1 ? "s" : ""),
                                TripMeFlightListActivity.class, NOTIFICATION_ID);
                        NotificationHelper.displayNotification(params);
                    }
                } else log(ex);
            }
        }.execute(options);
    }

    private boolean isAGoodTrip(Flight outbound, Flight inbound) {
        return inbound.getDeparture().getTime() - outbound.getArrival().getTime() > Utility.HOUR * 6
                && inbound.getArrival().getTime() - outbound.getDeparture().getTime() < Utility.HOUR * 18
                && inbound.getCoach().getSeatsAvailable() > 10
                && outbound.getCoach().getSeatsAvailable() > 10;
    }

    private boolean validOptions(TripMeOptions options, Boolean isScheduled) {
        Boolean enabled = options.isEnabled();
        Boolean validHomeBase = options.getHomeBase() != null && options.getHomeBase().length() == 3;
        Boolean validDestinations = options.getFavoriteDestinations() != null
                && options.getFavoriteDestinations().toArray().length > 0;
        Boolean validDay = false;
        for (String day : options.getSchedule())
            if (day.equals(Utility.DAY_OF_WEEK.format(new Date())))
                validDay = true;
        Boolean over18hours = (new Date().getTime() - getLastTripMeDate()) > Utility.HOUR * 18;

        log("Enabled = " + enabled
                + "; validHomeBase = " + validHomeBase
                + "; validDestinations = " + validDestinations
                + "; validDay = " + validDay
                + "; isScheduled = " + isScheduled
                + "; over18hours = " + over18hours);
        return enabled
                && validHomeBase
                && validDestinations
                && (validDay || !isScheduled)
                && (over18hours || !isScheduled);
    }

    private Long getLastTripMeDate(){
        Long time = PreferenceManager.getDefaultSharedPreferences(this).getLong(LAST_AUTOMATIC_TRIP_ME_SEARCH, 0);
        log("Last automatic Trip Me search: " + Utility.DATE_TIME.format(new Date(time)));
        return time;
    }

    private void updateLastTripMeDate(){

        Long current = new Date().getTime();
        log("Last automatic Trip Me search will be set to: " + Utility.DATE_TIME.format(new Date(current)));
        SharedPreferences settings = PreferenceManager
                .getDefaultSharedPreferences(this);
        SharedPreferences.Editor editor = settings.edit();
        editor.putLong(LAST_AUTOMATIC_TRIP_ME_SEARCH, current);
        editor.apply();
    }
}
